package net.yuyoo.uusession ;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import net.spy.memcached.AddrUtil; 
import net.spy.memcached.ConnectionFactory;
import net.spy.memcached.MemcachedClient;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * @author zhandl
 * CreateTime: 2008-9-29 02:01:15
 */
public class UUSessionServer {
	
	private static final Log log = LogFactory.getLog(UUSessionServer.class); 
	
	private Properties props = new Properties();
	
	/**
	 * 全局会话服务器的名称与hashcode
	 */
	private String serverName = null; 
	private String serverHashcode = null;
	
	/**
	 * 全局会话的实例名称与hashcode
	 */
	private String instanceName = null;
	private String instanceHashcode = null;
	
	/**
	 * MemcachedClient
	 */
	private MemcachedClient  mcClient = null; 
	
	/**
	 * 会话服务器最后更新时间戳的时间点的long值.
	 */
	private long lastServerUpdateStampTime = 0;
	
	/**
	 * 最后更新会话服务器时间戳的时间点的long值.
	 */
	private long lastInstanceUpdateStampTime = 0;
	
	/**
	 * 当前会话服务器的时间戳
	 */
	private long curServerStamp = -1;
	
	/**
	 * 会话过时时间,单位毫秒
	 */
	private long sessionTimeout = 0;
	private float updateFactor = 0;
	private long updateInterval = 0;
	
	/**
	 * 当前实例更新会话服务器信息调度器.
	 */
	private Timer syncStampTimer = null;
	
	/**
	 * Unix time 起点: 1970-1-1 0:00:00
	 */
	//private Date d_1970_01_01 = new Date(-28800000L);
	
	/**
	 * 可容忍时间误差值(millisecond)
	 */
	private long timeErrorTolerance = 0L;
	  
	public static synchronized UUSessionServer getInstance(String serverName) {
		
		return new UUSessionServer(serverName); 
	}
	
	private UUSessionServer (String serverName) {
	
		InputStream infile = null;
		try {
			// --- initial configulation information ---
			ClassLoader cl = Thread.currentThread().getContextClassLoader();
			infile = cl.getResourceAsStream("uusession.properties");
			this.props.load(infile);
			
			this.initMemcachedHosts();
			this.initServer(serverName);		
			this.initInstance(this.serverHashcode);
			
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		} finally {
			if (null != infile) {
				try { infile.close(); } catch (IOException e) { }
			}
		}
	}
	
	/**
	 * session id
	 * @return
	 */
	public synchronized String newSessionId() {
		 
		String now = Long.toString(System.currentTimeMillis());
		return now + "_" + this.instanceHashcode;
	}

	/**
	 * 获取Session
	 * @param id -- 全局会话ID 
	 * @return -- 全局会话对象
	 */
	public UUSession getSession(String id) {	
		
		 return new UUSession(this, id);
	}

	
	/**
	 * 初始化memcached配置.
	 *
	 */
	private void initMemcachedHosts() {
		
		String serverServiceList = null;
		
		try {
			serverServiceList = props.getProperty("serverServiceList", "127.0.0.1:11211");
			
			// ConnectionFactory bf = new BinaryConnectionFactory();
			this.mcClient = new MemcachedClient(AddrUtil.getAddresses(serverServiceList)); 

		} catch (Exception ex) {
			log.error(ex);
		}
	}
	
	
	
	/**
	 * 初始化会话服务器信息.
	 * @param sn
	 */
	private void initServer(String sn) {

		if (null == sn || "".equals(sn)) {
			this.serverName = UUID.randomUUID().toString();
		} else {
			this.serverName = sn;
		}
		this.serverHashcode = Integer.toString(this.serverName.hashCode());
		  
		this.mcClient.incr("count@" + this.serverHashcode, 1, 1);
		
		this.sessionTimeout = Long.parseLong(this.props.getProperty("sessionTimeout", "1800000"));
		this.updateFactor = Float.parseFloat(this.props.getProperty("updateFactor", "0.75"));
		this.updateInterval = (long) (this.sessionTimeout * this.updateFactor);
		this.timeErrorTolerance = Long.parseLong(this.props.getProperty("timeErrorTolerance", "1800000"));
		 
		// --- start a job task to update session server's STAMP and CURSTAMPTIME ---
		try {
			 
			SyncStampTask task = new SyncStampTask();
			syncStampTimer = new Timer("SyncStampTask Timer", true); 
			syncStampTimer.schedule(task, (long)(Math.random() * 5), this.updateInterval);
			
		} catch (Exception ex) {
			throw new RuntimeException("初始化更新会话服务器时间戳失败");
		}
		
	}
	
	/**
	 * 同步会话服务器信息.
	 * @param period
	 */
	public synchronized void syncStampTask() {
		
		log.info("开始更新当前实例的会话服务器的时间戳");
				
		String serverStamp = "stamp@" + this.serverHashcode;
		String lastServerUpdateStampTime = "lastServerUpdateStampTime@" + this.serverHashcode;
		
		Object ssRtv = this.mcClient.get(serverStamp);
		long cs = this.curServerStamp;
		long ss = -1;
		if (null != ssRtv) {
			ss = Long.parseLong((String)ssRtv); //mc.getCounter(serverStamp);
		}
		
				
		if (-1 == ss) {  // --- 会话服务器时间戳未初始化 ---
			
			log.info("会话服务器时间戳未初始化,ss:" + ss);
			
			this.curServerStamp = this.mcClient.incr(serverStamp, 1, 1);//mc.addOrIncr(serverStamp, 1); 
			this.lastServerUpdateStampTime = this.getCurSessionServerTime();
			this.lastInstanceUpdateStampTime = System.currentTimeMillis();
		    //	mc.set(lastServerUpdateStampTime, Long.toString(this.lastServerUpdateStampTime));
			this.mcClient.set(lastServerUpdateStampTime, 0, Long.toString(this.lastServerUpdateStampTime));
			
		} 
		
		if (-1 == cs) { // --- 当前实例未初始化 ---

			log.info("当前实例未初始化,cs:" + cs);
			
			this.curServerStamp = Long.parseLong((String)this.mcClient.get(serverStamp));//mc.getCounter(serverStamp);
			Object rtv = this.mcClient.get(lastServerUpdateStampTime);
			this.lastServerUpdateStampTime = Long.parseLong(rtv.toString());
			this.lastInstanceUpdateStampTime = System.currentTimeMillis();			
			
		} else if (cs == ss) {  // --- 会话服务器时间戳 未 被其他实例更新信息 --- 

			log.info("会话服务器时间戳 未 被其他实例更新信息, cs:" + cs);
			
			this.curServerStamp = this.mcClient.incr(serverStamp, 1, 1); //mc.addOrIncr(serverStamp, 1);	
			this.lastServerUpdateStampTime = this.getCurSessionServerTime();
			//mc.set(lastServerUpdateStampTime, Long.toString(this.lastServerUpdateStampTime));
			this.mcClient.set(lastServerUpdateStampTime, 0, Long.toString(this.lastServerUpdateStampTime));
			this.lastInstanceUpdateStampTime = System.currentTimeMillis();
			
		} else if ( (cs + 1) == ss) {  // --- 会话服务器时间戳 已 被其他实例更新信息 ---

			log.info("会话服务器时间戳 已 被其他实例更新信息, cs:" + cs);
			
			this.curServerStamp = ss;		
			Object rtv = this.mcClient.get(lastServerUpdateStampTime);
			this.lastServerUpdateStampTime = Long.parseLong(rtv.toString());
			this.lastInstanceUpdateStampTime = System.currentTimeMillis();
			
		} else if (cs > ss || cs < (ss - 1) ){  // --- 会话服务器时间戳非法 ---
			
			String errorMessage = "更新服务器时间戳异常,发现非法时间戳: [curServerStamp: " + curServerStamp +", serverHashcode: " + serverHashcode + "]";
			throw new RuntimeException(errorMessage);
		} 
		  
		log.info("完成更新服务器时间戳");
	}
	
	/**
	 * 启动同步会话服务器信息任务内部类.
	 * @author zhandl
	 * CreateTime: 2008-9-30 涓婂崍02:59:43
	 */
	private class SyncStampTask extends TimerTask {		  		
		@Override
		public void run() {
			syncStampTask();
		}		
	}
	
	
	/**
	 * 初始化实例
	 * @param sc
	 * @return
	 */
	private void initInstance(String sc) {
		 
		this.instanceName = UUID.randomUUID().toString();
		this.instanceHashcode = Integer.toString(this.instanceName.hashCode());	
		
		// --- 保存实例的名称 ---
		String newCode = sc + this.instanceHashcode;		
		//mc.set(newCode, this.instanceName); 
		this.mcClient.set(newCode, 0, this.instanceName);
	}
	
	/**
	 * 鑾峰彇Memcached client瀵硅薄
	 * @return
	 */
	public MemcachedClient getMemcachedClient() {		
		return this.mcClient;
	}

	/**
	 * 获取当前会话服务器的时间戳.
	 * @return
	 */
	public long getCurServerStamp() {
		return this.curServerStamp;
	} 
	
	/**
	 * 返回会话服务器最后更新时间戳的时间点.
	 * @return
	 */
	public long getLastServerUpdateStampTime() {
		return this.lastServerUpdateStampTime;
	}
	
	/**
	 * 返回实例最后更新会话服务器时间戳的时间点.
	 * @return
	 */
	public long getLastInstanceUpdateStampTime() {
		return this.lastInstanceUpdateStampTime;
	}
	
	/**
	 * 返回会话服务器会话过去时长.
	 * @return
	 */
	public long getSessionTimeout() {
		return this.sessionTimeout;
	}

	/**
	 * 返回更新数据间隔.
	 * @return
	 */
	public long getUpdateInterval() {
		return updateInterval;
	} 
	
	
	/**
	 * 获取会话服务器当前时间.
	 * @return
	 */
	public long getCurSessionServerTime() {

		/*
		MemCachedClient mc = this.getMemCachedClient();
		Map<String, Object> stats = mc.stats();
		Iterator<String> it = stats.keySet().iterator();
		String serverKey = null;
		Map serverStats = null;
		String tarTime = "0";
		String curTime = null;
		while (it.hasNext()) {
			serverKey = it.next();
			serverStats = (Map) stats.get(serverKey);
			curTime = (String) serverStats.get("time");
			if (tarTime.compareTo(curTime) < 0) {
				tarTime = curTime;
			}
		}
		long tar = Long.parseLong(tarTime) * 1000;		
		return this.d_1970_01_01.getTime() + tar;
		*/
		return  System.currentTimeMillis();
	}
	
	/**
	 * 获取时间误差容忍值(millisecond)
	 * @return
	 */
	public long getTimeErrorTolerance() {
		return this.timeErrorTolerance;
	}
		
	/**
	 * @param args
	 */
	public static void main(String[] args) { 

		UUSessionServer s = UUSessionServer.getInstance("yuyoo");
				
		log.info("count@115351421: " + s.mcClient.get("count@115351421"));
		log.info("stamp@115351421: " + s.mcClient.get("stamp@115351421"));
		Map stats = s.mcClient.getStats();
		log.info("stats: " + stats);
		 
	}
}
